package com.dude.utils;


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;

import com.dude.interfaces.IMemoryService;
import com.dude.objects.GenericPair;
import com.dude.objects.GenericTriplet;

import android.content.Context;
import android.graphics.Bitmap;
import android.media.ThumbnailUtils;


/** this class should include all general I/O methods, to be called from the various  
 *  activities. 
 */
public class FilesOperations implements IMemoryService {
	
	//=======================global constants================================
	protected int max_num_of_pictures = 4;
	// the Context used in the following methods
	protected static Context ctxt;
	// the width and height of an image returned as a thumbnail
	protected int thumbnailSize = 100;
	

	
	/* constructor to be called with the instance of the calling Context (Activity, Service, etc.)
	 * must call it before call to any other method from this class.
	 */
	public FilesOperations(Context ctxt)
	{
		FilesOperations.ctxt=ctxt;
	}
		
		
	/* method for getting the names of all objects in file system.
	 * singleObjects and multiObjects must be valid ArrayLists at time of call. at the end of 
	 * the method, they will include the names of all single and multi objects respectively.
	 * on failure returns false.
	 */
	public boolean getAllObjectsNames(ArrayList<String> singleObjects, ArrayList<String> multiObjects)
	{			
		//get the root directory for the application
		File myDir = ctxt.getDir(dirName, 0);
		
		// get both sub directories
		File singlesDir = new File(myDir, singlesDirName);
		File multiDir = new File(myDir, multiDirName);
		if (!singlesDir.exists())
			if (!singlesDir.mkdirs())
				return false;
		if (!multiDir.exists())
			if (!multiDir.mkdirs())
				return false;
		
		// traverse all objects' files in the directories
		String [] singlesFiles = singlesDir.list();
		if (singlesFiles==null)
			//  this abstract pathname does not denote a directory, or if an I/O error occurs
			return false;
		else
			for (String str : singlesFiles)  // every file name in the directory represents an object
				singleObjects.add(str);
		
		String [] multiFiles = multiDir.list();
		if (multiFiles==null)
			//  this abstract pathname does not denote a directory, or if an I/O error occurs
			return false;
		else
			for (String str : multiFiles)  // every file name in the directory represents an object
				multiObjects.add(str);
		
		return true;
	}
	
	
	
	/* the method returns true if no single object with name "name" already exists in memory,
	 *  else it returns false
	 */
	public boolean isNameFreeForSingleObject(String name)
	{
		File objFile = getObjectFile(name, true);
		if ((objFile==null)||(objFile.exists()))
			return false;
		else		
			return true;
	}
	
	
	/* the method returns true if no multi object with name "name" already exists in memory,
	 *  else it returns false
	 */
	public boolean isNameFreeForMultiObject(String name)
	{
		File objFile = getObjectFile(name, false);
		if ((objFile==null)||(objFile.exists()))
			return false;
		else		
			return true;
	}
		
	
	/* the method renames the multi object called oldName to newName. it assumes such object
	 * exists at the time of the call to the method. 
	 * if it succeeds it returns true, else it returns false.
	 */
	public boolean renameMultiObject(String oldName, String newName)
	{
		// get the object's folder
		File objectsFolder = getObjectFile(oldName, false);
		if ((objectsFolder==null)||(!objectsFolder.exists()))
			return false;
		// create new path to objects' folder
		String newPath = objectsFolder.getParent()+File.separator+newName;
		
		//rename objects' folder
		File newFile = new File(newPath);
		if (!objectsFolder.renameTo(newFile))
			return false;
		
		return true;
	}
	
	
	/* the method renames the single object called oldName to newName. it assumes such object
	 * exists at the time of the call to the method. 
	 * if it succeeds it returns true, else it returns false.
	 */
	public boolean renameSingleObject(String oldName, String newName)
	{		
		// get the object's folder
		File objectsFolder = getObjectFile(oldName, true);
		if ((objectsFolder==null)||(!objectsFolder.exists()))
			return false;
		
		// create new path to objects' folder
		String newPath = objectsFolder.getParent()+File.separator+newName;
		
		//rename objects' folder
		File newFile = new File(newPath);
		if (!objectsFolder.renameTo(newFile))
			return false;
		
		// check if object appears in any multi object, and if so fix the name appearing there
		File [] multiObjects = getAllMultiObjectsFiles();
		if (multiObjects==null)
			return false;
		
		File singles = null;
		BufferedReader br;
		String strLine;
		ArrayList<File> filesToUpdate = new ArrayList<File>();
		for (File file : multiObjects)
		{
			singles = new File(file, innerSinglesFileName);
			if (!singles.exists())
			{
				try {
					singles.createNewFile();
				} catch (IOException e) {
					return false;
				}
				continue;
			}
			// read text file line by line - each line corresponds to a single object
			try{
				br = new BufferedReader(new FileReader(singles));
				while ((strLine=br.readLine())!=null)
					if ((strLine!="")&&(strLine.equals(oldName)))						
						filesToUpdate.add(file);																
				br.close();
			} catch (IOException e) {
				return false;
			}
		}
		for (File f : filesToUpdate)
		{
			File singlesFile  =  new File(f, innerSinglesFileName);
			if (!replaceLineInFile(oldName,newName, singlesFile, false, -1))
				return false;			
		}						
		return true;		
	}
	
	
	/* helper function - replaces in file every line equals to oldLine by newLine, unless
	 * deleteLine==true, then it just delete oldLine from file. if num!=-1 only num first
	 * occurrences of oldLine will be replaced
	 */
	protected boolean replaceLineInFile(String oldLine, String newLine, File file,
																boolean deleteLine, int num)
	{	
		//Construct the new file that will later be renamed to the original objects' names filename
		File tempFile = new File(file.getAbsolutePath()+".tmp");		
		try{
			BufferedReader br = new BufferedReader(new FileReader(file));
		    BufferedWriter bw = new BufferedWriter(new FileWriter(tempFile));
		    String line = null;
		    int i=0;		    
		    //Read from the original file and write to the new
		    //unless content matches data to be removed
		    boolean keepReplacing = true;
		    while ((line = br.readLine()) != null) {
		        if ((!line.equals(oldLine))||(!keepReplacing))
		        {
		        	bw.write(line);
		        	bw.newLine();
		        }		          	        
		        else
		        {
		        	if (!deleteLine)
		        	{
		        		bw.write(newLine);
		        		bw.newLine();
		        	}
		        	++i;
		        }
		        if ((num!=-1)&&(i==num))
		        	keepReplacing = false;       
		    }
		    bw.close();
		    br.close();
		    
		    //Delete the original file
		    if (!file.delete()) {
		        return false;
		    } 
		    //Rename the new file to the filename the original file had.
		    if (!tempFile.renameTo(file)){
		    	return false;
		    }		        		      
		} catch(IOException e) {
			return false;
		}
				
		return true;
	}
	

	
	/* the method returns an ArrayList with all the comments for the specified object.
	 * if it fails, it returns null.
	 */
	public ArrayList<String> getAllCommentsForObject(String objName, boolean isSingle)
	{
		// get the object's folder
		File objectsFolder = getObjectFile(objName, isSingle);
		if ((objectsFolder==null)||(!objectsFolder.exists()))
			return null;

		File commentsFile = new File(objectsFolder, objectDataFileName);
		if (!commentsFile.exists())
			return null;		
		//start reading from comments' file. every line is a comment
		String strLine;
		ArrayList<String> comments = new ArrayList<String>();
		try{
			BufferedReader br = new BufferedReader(new FileReader(commentsFile));
			if (isSingle)
			{
				// 2 first lines correspond to data about sounds
				strLine=br.readLine();	
				strLine=br.readLine();	 
			}
			// all next lines are comments
			while ((strLine=br.readLine())!=null)
				if (strLine!="")
					comments.add(strLine);
			br.close();
		} catch (IOException e) {
			return null;
		}
	
		return comments;	
	}
	

	/* the method adds a comment to the comments' file of the given object.
	 * if it fails or there is no object with the given name in the system, it returns false.
	 */
	public boolean addCommentForObject(String objName, boolean isSingle, String comment)
	{		
		// get the object's folder
		File objectsFolder = getObjectFile(objName, isSingle);	
		if ((objectsFolder==null)||(!objectsFolder.exists()))
			return false;

		// get the comments' file
		File commentsFile = new File(objectsFolder, objectDataFileName);
		if (!commentsFile.exists())
			return false;
		FileWriter fw;
		BufferedWriter bw;
		// try writing new comment to end of comments' file
		try{
			fw = new FileWriter(commentsFile, true);
			bw = new BufferedWriter(fw);
			bw.write(comment);	
			bw.newLine();
			bw.close();
			fw.close();
		} catch(IOException e) {
			return false;
		}
		
		return true;	
	}
	
		
	/* the method removes the comment from the comments' file of the given object.
	 * if it fails it returns false.
	 */
	public boolean removeComment(String objName, boolean isSingle, String comment)
	{		
		// get the object's folder
		File objectsFolder = getObjectFile(objName, isSingle);
		if ((objectsFolder==null)||(!objectsFolder.exists()))
			return false;

		// get the comments' file
		File commentsFile = new File(objectsFolder, objectDataFileName);
		if (!commentsFile.exists())
			return false;	
				
		if (!replaceLineInFile(comment, null, commentsFile, true, 1))
			return false;
				
		return true;		
	}
	
	
	/* the method returns the width and height parameter used in ThumbnailUtils.extractThumbnail
	 * to return a thumnbnail of an image
	 */
	public int getThumbnailSize()
	{
		return thumbnailSize;
	}
	
	
	/* the method sets the width and height parameter used in ThumbnailUtils.extractThumbnail
	 * to return a thumnbnail of an image
	 */
	public void setThumbnailSize(int s)
	{
		this.thumbnailSize=s;
	}
	
	
	/* the method saves pic as a picture for the single object with name objName.
	 * if this is the first picture added to the object, then it is automatically the profile picture.
	 * it assumes the object doesn't yet have the maximum number of pictures allowed, and that
	 * objName is a valid object name.
	 * if it succeeds it returns the name of the new image, else it returns null.
	 */
	public String addImage(String objName, Bitmap pic) 
	{
		// get the object's folder
		File objectsFolder = getObjectFile(objName, true);
		// get the images folder (it must exist by now)
		File imagesFolder = new File(objectsFolder, imageFolderName);
		// find the name for the new image
		String name = getNameForImage(imagesFolder);
		File image = new File(imagesFolder, name+".jpg");									
		// add pic to object's folder
		try{
			image.createNewFile();
			FileOutputStream fos = new FileOutputStream(image);
			if (!pic.compress(Bitmap.CompressFormat.JPEG, 100, fos))
			{
				image.delete();
				return null;
			}
			fos.flush();
			fos.close();				
		}
		catch(IOException e){
			return null;
		}					

		return name;
	}
	
	
	/* a helper method to decide on the new name for an image, given the folder of pictures
	 * of the object.
	 */
	protected String getNameForImage(File imagesFolder)
	{
		String [] pics = imagesFolder.list();
		int num = pics.length;
		boolean found = false, taken = false;;
		while (!found)
		{
			for (int i=0; i<pics.length; ++i)
			{
				if (pics[i].equals("pic"+num+".jpg"))
				{
					taken = true;
					break;
				}
			}
			if (!taken)		//found a free name				
				found = true;			
			else
			{
				taken = false;
				++num;
			}				
		}
		return "pic"+num;
	}
	
	
	/* the method copies the image at the given path to the object's folder.
	 * if it fails it returns null, else it returns the image's name
	 */
	public String addPictureFromPath(String objName, String path)
	{
		// get the object's folder
		File objectsFolder = getObjectFile(objName, true);
		// get the images folder (it must exist by now)
		File imagesFolder = new File(objectsFolder, imageFolderName);
		// find name of image
		String imgNameWithJPG = (new File(path)).getName();
		String imgName = imgNameWithJPG.substring(0, imgNameWithJPG.length()-4);
		File imgFile = new File(imagesFolder, imgName+".jpg");
		try{
		    InputStream in = new FileInputStream(path);
		    OutputStream out = new FileOutputStream(imgFile);
		    // Transfer bytes from in to out
		    byte[] buf = new byte[512];
		    int len;
		    while ((len = in.read(buf)) > 0) {
		        out.write(buf, 0, len);
		    }
		    in.close();
		    out.close();
		}catch(Exception e){
			return null;
		}
		return imgName;		
	}
	
	
	
	/* the method returns true of the single object already has the maximum allowed number of 
	 * pictures, as defined by a constant. else it returns false. 
	 * $pre: objName represents a valid object name
	 */
	public boolean hasMaxNumOfPictures(String objName)
	{
		// get the object's folder
		File objectsFolder = getObjectFile(objName, true);
		// get the images folder (it must exist by now)
		File imagesFolder = new File(objectsFolder, imageFolderName);
		File [] images = imagesFolder.listFiles();
		if (images.length>=max_num_of_pictures)
			return true;
		
		return false;		
	}

		
	/* the method returns a GenricPair comprising of the name of the profile's picture  and a 
	 * Bitmap of that picture, of the single object named objName, if such exists.
	 * if no such picture exists it returns null.	
	 * if (minimize==true) it sub-samples the picture and returns a thumbnail
	 * it assumes objName is valid name. 
	 * upon failure it throws FOException. 
	 * the profile picture is the one whose name appears in first line in data file
	 */
	public GenericPair<String, Bitmap> getProfileImage(String objName, boolean minimize)throws FOException
	{		
		// get the object's folder
		File objectsFolder = getObjectFile(objName, true);
		// get the images folder (it must exist by now)
		File imagesFolder = new File(objectsFolder, imageFolderName);
		if (!imagesFolder.exists())
			throw new FOException("Image's folder doesn't exist");
		//check if there arepictures in folder
		String [] pictures = imagesFolder.list();
		if (pictures==null)
			throw new FOException("Image's folder is invalid");
		if (pictures.length==0)
			return null;
		// first image in folder will be returned
		File image  = new File(imagesFolder, pictures[0]);
		if (!image.exists())
			throw new FOException("Picture Missing From Folder");
		Bitmap pic = BitmapManager.decodeBitmapFromFile(image.getAbsolutePath(), 256, 200);
		if (minimize)
			pic  = ThumbnailUtils.extractThumbnail(pic, thumbnailSize, thumbnailSize);
		// remove .jpg ending from picture's name
		String name = pictures[0].substring(0, pictures[0].length()-4);
    	GenericPair<String, Bitmap> result = new GenericPair<String, Bitmap>(name, pic);
    	return result;
	}
	
		
	/* the method changes the name of a picture belonging to object objName.
	 * the picture named oldPicName is renamed newPicName
	 * if the method fails it returns 3, if another picture with this name already exists
	 * it returns 2 (and doesn't do the rename), if it succeeds it returns 1.
	 * oldPicName and newPicName without ending .jpg
	 */
	public int renamePicture(String objName, String oldPicName, String newPicName)
	{
		// get the object's folder
		File objectsFolder = getObjectFile(objName, true);
		if ((objectsFolder==null)||(!objectsFolder.exists()))
			return 3;
		// get the images folder (it must exist by now)
		File imagesFolder = new File(objectsFolder, imageFolderName);
		if (!imagesFolder.exists())
			return 3;
		
		File image  = new File(imagesFolder, oldPicName+".jpg");
		// create new path to the picture
		String newPath = image.getParent()+File.separator+newPicName+".jpg";
		// check if a picture with this name already exists
		File newPic = new File(newPath);
		if (newPic.exists())
			return 2;
		// rename the picture		
		if (!image.renameTo(newPic))
			return 3;
		return 1;
	}
	
	
	/* the method returns all an array of all the pictures+names of the single object with name 
	 * objName. 
	 * if (minimize==true) it sub-samples the picture and returns a thumbnail
	 * it assumes objName is a valid name of an existing object.
	 */
	public ArrayList<GenericPair<String,Bitmap>> getAllPictures(String objName, boolean minimize)
	{		
		// get the object's folder
		File objectsFolder = getObjectFile(objName, true);
		// get the images folder (it must exist by now)
		File imagesFolder = new File(objectsFolder, imageFolderName);
		File [] images = imagesFolder.listFiles();		
		ArrayList<GenericPair<String,Bitmap>> result = new ArrayList<GenericPair<String,Bitmap>>();
		for (int i=0; i<images.length; ++i)
		{
			File image= images[i];
	    	Bitmap pic = BitmapManager.decodeBitmapFromFile(image.getAbsolutePath(), 256, 200);
	    	if (minimize)
	    		pic  = ThumbnailUtils.extractThumbnail(pic, thumbnailSize, thumbnailSize);
	    	//get picture's name
	    	String name = image.getName();
	    	// remove the .jpg ending from name
	    	name = name.substring(0, (name.length()-4));
	    	result.add(new GenericPair<String, Bitmap>(name, pic));			
		}	
				
		return result;
	}
	
	
	
	/* remove the picture with name picName from the pictures saved for the single object.
	 * it assumes objName and picName are valid. if the picture is the profile's picture, a different
	 * picture is chosen for the profile (if another one exists for the object)
	 * if it fails it returns false.
	 * picName - without ending.jpg
	 */
	public boolean removeImage(String objName, String picName)
	{
		// get the object's folder
		File objectsFolder = getObjectFile(objName, true);
		// get the images folder (it must exist by now)
		File imagesFolder = new File(objectsFolder, imageFolderName);		
		// get image file
		File image = new File(imagesFolder, picName+ ".jpg");	
		if (image.exists())
			if (!image.delete())  //delete the image
				return false;						

		return true;
	}
	
	
	
	/* the method adds a new object by the name objName to the file system.
	 * if ($ret==1) then operation succeeded. if ($ret==2) then object by the same name
	 *  already exists and operation aborted. if ($ret==3) then operation failed
	 */
	public int addObject(String objName, boolean isSingle)
	{
		File myDir;
		File objectsFolder;
		
		//get the root directory for the application
			myDir = ctxt.getDir(dirName, 0);
		// get appropriate sub directory
		File dir;
		if (isSingle)
			dir = new File(myDir, singlesDirName);
		else 
			dir = new File(myDir, multiDirName);
		if (!dir.exists())
			if (!dir.mkdirs())
				return 3;

		// create sub-folder for new object
		objectsFolder = new File(dir, objName);
		if (objectsFolder.exists())
			return 2;
		if (!(objectsFolder.mkdirs()))
			return 3;		
		//create an empty file for comments 
		File commentsFile = new File(objectsFolder, objectDataFileName);		
		try {
			commentsFile.createNewFile();
			// if this is a single object create a sub folder for images
			if (isSingle)
			{
				File imagesFolder = new File(objectsFolder, imageFolderName);
				if (!imagesFolder.mkdirs())
				{
					// cancel the folders already created
					commentsFile.delete();
					objectsFolder.delete();
					return 3;
				}	
				// add 2 lines regarding initial sound's data
				FileWriter fw;
				BufferedWriter bw;
				fw = new FileWriter(commentsFile, false);
				bw = new BufferedWriter(fw);
				bw.write(usesDefaultSoundString);
				bw.newLine();
				bw.write(noSoundPathExists);
				bw.newLine();
				bw.close();
				fw.close();								
			}
			else
			{
				File singlesFile = new File(objectsFolder, innerSinglesFileName);
				if (!singlesFile.createNewFile())
				{
					// cancel the folders already created
					commentsFile.delete();
					objectsFolder.delete();
					return 3;				
				}
			}
		} catch (IOException e) {
			return 3;
		}
		
		return 1;		
	}
	
		
	/* the method deletes the object with the name objName and all associated data from the 
	 * file system. if it fails it returns false.
	 */
	public boolean removeObject(String objName, boolean isSingle)
	{
		// get the object's folder
		File objectsFolder = getObjectFile(objName, isSingle);
		if (objectsFolder==null)
			return false;
		if (!objectsFolder.exists()) // the object wasn't in memory to begin with
			return true;
		
		// if single object, need to check and update any multi objects it is included in
		if (isSingle)
		{			
			// check if any multi object includes the object, and if so delete it from the multi
			File [] multiObjects = getAllMultiObjectsFiles();
			if (multiObjects==null)
				return false;
			File singles = null;
			BufferedReader br;
			String strLine;
			ArrayList<File> filesToUpdate = new ArrayList<File>();
			for (File object : multiObjects)
			{
				singles = new File(object, innerSinglesFileName);
				if (!singles.exists())
					continue;					
				// read text file line by line - each line corresponds to a single object
				try{
					br = new BufferedReader(new FileReader(singles));
					while ((strLine=br.readLine())!=null)
						if ((strLine!="")&&(strLine.equals(objName)))
							filesToUpdate.add(object);										
					br.close();
				} catch (IOException e) {
					return false;
				}
			}
			
			for (File f: filesToUpdate)
			{
				File singlesFile  = new File(f, innerSinglesFileName);
				if (!replaceLineInFile(objName, null, singlesFile, true, -1))
					return false;							
			}	
		}		
		return recursiveFolderDelete(objectsFolder);		
	}
	
	
	/* the method adds objName as a single object included in the multi object.
	 * it assumes that multiName and objName are valid names of a multi and a single object
	 * respectively.
	 * if it fails it returns false.
	 * if object is already a part of multi object it returns true, and doesn't add it again.
	 */
	public boolean addObjectToMultiObject(String multiName, String objName)
	{
		File multiFile  = getObjectFile(multiName, false);
		File singles = new File(multiFile, innerSinglesFileName);
		if (!singles.exists())
				return false;

		// check if object is already in multi object
		boolean alreadyIn = false;		
		BufferedReader br;
		String strLine;	
		try
		{
			br = new BufferedReader(new FileReader(singles));
			while ((strLine=br.readLine())!=null)				
				if (strLine.equals(objName))
				{
					alreadyIn = true;
					break;
				}										
			br.close();
		}catch(Exception e){
			return false;
		}
		if (alreadyIn)
			return true;
				
		FileWriter fw;
		BufferedWriter bw;
		// try writing single object name to end of objects file in multi folder
		try{
			fw = new FileWriter(singles, true);
			bw = new BufferedWriter(fw);
			bw.write(objName);		
			bw.newLine();
			bw.close();
			fw.close();
		} catch(IOException e) {
			return false;
		}
		
		return true;
	}

	
	/* this method removes objName from the multi object denoted by multiName. 
	 * it assumes both names are valid objects' names.
	 * if it fails it returns false.
	 */
	public boolean removeObjectFromMultiObject(String multiName, String objName)
	{
		File multiFile  = getObjectFile(multiName, false);
		File singles = new File(multiFile, innerSinglesFileName);
		if (!singles.exists())
				return false;
		// delete object's bane from singles' file
		if (!replaceLineInFile(objName, null, singles, true, 1))
			return false;	
		
		return true;
	}
	
	/* the method returns an array with the names of all multi or single objects in the memory,
	 * depending on whether singles==false or singles==true
	 * if the method fails it returns null
	 */
	public String [] getOneTypeObjectsNames(boolean singles)
	{		
		File myDir = ctxt.getDir(dirName, 0);
		if (myDir==null)
			return null;
		File dir;
		if (singles)
			dir =  new File(myDir, multiDirName);
		else
			dir = new File(myDir, singlesDirName);
		if ((dir==null)||(!dir.exists()))
			return null;
		return dir.list();
	}
	
	
	/* the method returns a list of all single objects' names and pictures that who are included
	 * in the given multi object.
	 * it assumes multiName is a valid multi object's name. 
	 * if it fails it returns null
	 */
	public  ArrayList<GenericPair<String, Bitmap>> getAllIncludedObjects(String multiName)
	{
		ArrayList<GenericPair<String, Bitmap>> result = new ArrayList<GenericPair<String, Bitmap>>();
		// get the object's folder
		File objectsFolder = getObjectFile(multiName, false);
		// get the single objects names file
		File singlesFile  = new File(objectsFolder, innerSinglesFileName);
		if (!singlesFile.exists())
			return null;
		
		//read file line by line, each line is a name of an object
		String strLine;
		GenericPair<String, Bitmap> profilePic;		
		try{			
			BufferedReader in = new BufferedReader(new FileReader(singlesFile));
			while ((strLine=in.readLine())!=null)
				if (strLine!="")
				{					
					// find the single object's picture
					profilePic = getProfileImage(strLine, true);
					if (profilePic!=null)
						result.add(new GenericPair<String, Bitmap>(strLine, profilePic.getSecond()));
					else
						result.add(new GenericPair<String, Bitmap>(strLine, null));
				}								
			in.close();
		} catch (Exception e) {
			return null;
		}
		return result;		
	}
	
	
	
	/* the method returns true if the single object represented by objName uses the default sound
	 * and false otherwise. it assumes the object exists.
	 * return int - 1 for uses default, 2 for not uses default, 3 for failure
	 */
	public int getUsesDefaultSound(String objName)
	{
		// get the object's folder
		File objectsFolder = getObjectFile(objName, true);		
		File dataFile = new File(objectsFolder, objectDataFileName);
		if (!dataFile.exists())
			return 3;
		int result;
		try{			
			String line=null;
			BufferedReader reader = new BufferedReader(new FileReader(dataFile));
			// first line in data file is the necessary data
			line=reader.readLine();
			if (line==null)
				return 3;
			if (line.equals(usesDefaultSoundString))
				result=1;
			else
				if (line.equals(notUsesDefaultSoundString))
					result=2;
				else 
					result=3;			
			reader.close();
		}catch(Exception e){
			return 3;
		}
		return result;
	}
	
		
	
	 /* the method sets whether the single object represented by objName uses the default sound.
	  * it returns false if it failed.
	 */
	public boolean setUsesDefaultSound(String objName, boolean usesDefault)
	{
		// get the object's folder
		File objectsFolder = getObjectFile(objName, true);		
		File dataFile = new File(objectsFolder, objectDataFileName);
		if (!dataFile.exists())
			return false;
		try{			
			String line=null;
			BufferedReader reader = new BufferedReader(new FileReader(dataFile));	
			// first line in data file decides use of default sound
			line=reader.readLine();
			if (line==null)
				return false;
			if (line.equals(usesDefaultSoundString)&&(!usesDefault))			
				if (!replaceLineInFile(usesDefaultSoundString, notUsesDefaultSoundString, dataFile,
						false, 1))
					return false;
			if (line.equals(notUsesDefaultSoundString)&&(usesDefault))
					if (!replaceLineInFile(notUsesDefaultSoundString, usesDefaultSoundString, dataFile,
							false, 1))
						return false;
			reader.close();
		}catch(Exception e){
			return false;
		}		
		return true;
	}
	
		
	/* the method returns a string representing the path of the sound associated with the single
	 * object named objName. if it fails it returns null.
	 * -- the method should only be called if objectHasSound(objName)==true
	 */
	public String getSoundPath(String objName)
	{
		String path = null; 
		// get the object's folder
		File objectsFolder = getObjectFile(objName, true);		
		File dataFile = new File(objectsFolder, objectDataFileName);
		if (!dataFile.exists())
			return null;
		try{			
			String line=null;
			BufferedReader reader = new BufferedReader(new FileReader(dataFile));	
			// second line in data file is path, if such exists
			line=reader.readLine();			
			line=reader.readLine();		
			if (!line.equals(noSoundPathExists))
				path=line;
			reader.close();
		}catch(Exception e){
			return null;
		}				
		return path;
	}
	
	
	
	/* the method returns true if the single object with the name objName has a sound. 
	 * else it returns false. it assumes the object's name is valid.
	 */
	public boolean objectHasSound(String objName)
	{
		boolean result = false;
		// get the object's folder
		File objectsFolder = getObjectFile(objName, true);		
		File dataFile = new File(objectsFolder, objectDataFileName);
		if (!dataFile.exists())
			return false;		
		try{			
			String line=null;
			BufferedReader reader = new BufferedReader(new FileReader(dataFile));	
			// second line in data file is path, if such exists
			line = reader.readLine();			
			line=reader.readLine();			
			if (!line.equals(noSoundPathExists))
				result=true;			
			reader.close();
		}catch(Exception e){			
			return false;
		}		
		return result;
	}
	
	
	/* the method sets path as the path of the sound associated with objname. if the object already 
	 * had a sound, the new path overrides it.
	 * if it fails it returns false.
	 */
	public boolean setSoundPath(String objName, String path)
	{		
		// get the object's folder
		File objectsFolder = getObjectFile(objName, true);		
		File dataFile = new File(objectsFolder, objectDataFileName);
		if (!dataFile.exists())
			return false;		
		try{			
			String line=null;
			BufferedReader reader = new BufferedReader(new FileReader(dataFile));	
			// second line in data file is the sound's path
			line=reader.readLine();		
			line=reader.readLine();			
			if (line==null)
				return false;
			reader.close();		
			
			//change the sound's path
			if (!replaceLineInFile(line, path, dataFile, false, 1))
				return false;			
		}catch(Exception e){			
			return false;
		}		
		return true;
	}
	

	/* the method deletes all single objects from memory. if it fails it returns false.
	 */
	public boolean deleteAllSingleObjects()
	{		
		File myDir = ctxt.getDir(dirName, 0);
		if (myDir==null)
			return false;
		File dir = new File(myDir, singlesDirName);
		if (!dir.exists())
			return true;
		return recursiveFolderDelete(dir);
	}
	
		
	/*  the method deletes all multi objects from memory. if it fails it returns false.
	 */
	public boolean deleteAllMultiObjects()
	{
		File myDir = ctxt.getDir(dirName, 0);
		if (myDir==null)
			return false;
		File dir = new File(myDir, multiDirName);
		if (!dir.exists())
			return true;
		return recursiveFolderDelete(dir);
	}
	
	
	
	/* helper method for recursive deletion of files
	 */
	protected boolean recursiveFolderDelete(File dir)
	{
	    if (dir.isDirectory()) {
	        String[] children = dir.list();
	        for (int i=0; i<children.length; i++) {
	            boolean success = recursiveFolderDelete(new File(dir, children[i]));
	            if (!success) 
	                return false;	            
	        }
	    }
	    // The directory is now empty so delete it
	    return dir.delete();
	}
	
	
	/* the method the returns a reference to the folder of the object with the name objName,
	 * if such exits. else it returns null
	 */
	protected File getObjectFile(String objName, boolean isSingle)
	{
		File myDir = ctxt.getDir(dirName, 0);
		if (myDir==null)
			return null;
		File objectsFolder=null;
		// get appropriate sub directory
		File dir;
		if (isSingle)
			dir = new File(myDir, singlesDirName);
		else 
			dir = new File(myDir, multiDirName);
		if ((dir==null)||(!dir.exists()))
			return null;
			
		// get the object's folder
		objectsFolder = new File(dir, objName);		
		return objectsFolder;		
	}
	
	
	/* helper method
	 */
	protected File [] getAllMultiObjectsFiles()
	{
		File myDir = ctxt.getDir(dirName, 0);
		if (myDir==null)
			return null;
		File dir =  new File(myDir, multiDirName);
		if (!dir.exists())
			if (!dir.mkdirs())
				return null;
		File [] files = dir.listFiles();
		return files;
	}

	
	/* a custom exception 
	 */
	public class FOException extends Exception
	{
		String mistake;		
		public FOException()
		{
			super();
			mistake="Unknown";
		}
		
		public FOException(String err)
		{
			super(err);
			mistake=err;
		}
		
		public String getError()
		{
			return mistake;
		}		
	}


	@Override
	public ArrayList<GenericTriplet<String, Bitmap, Boolean>> getAllObjects() {
		
		ArrayList<String> singleObjects = new ArrayList<String>();
		ArrayList<String> multiObjects = new ArrayList<String>();
		if (!getAllObjectsNames(singleObjects, multiObjects))
			return null;
		
		ArrayList<GenericTriplet<String, Bitmap, Boolean>> result = 
						new ArrayList<GenericTriplet<String, Bitmap, Boolean>>();
		GenericPair<String, Bitmap> gp;
		for (String s : singleObjects)
		{
			try{
				gp = getProfileImage(s, true);
				if (gp==null)
					result.add(new GenericTriplet<String, Bitmap, Boolean>(s, null, true));
				else
					result.add(new GenericTriplet<String, Bitmap, Boolean>(s, gp.getSecond(), true));
			}catch(FOException e){
				return null;
			}			
		}
	
		ArrayList<GenericPair<String, Bitmap>> inObjects;
		for (String s : multiObjects)
		{
			inObjects =  getAllIncludedObjects(s);
			if (inObjects==null)
				return null;
			if (inObjects.isEmpty())
				result.add(new GenericTriplet<String, Bitmap, Boolean>(s, null, false));
			else
			{
				boolean hasPic = false;
				for (GenericPair<String, Bitmap> obj : inObjects)
				{
					if (obj.getSecond()!=null)
					{
						result.add(new GenericTriplet<String, Bitmap, Boolean>(s, obj.getSecond(), false));
						hasPic=true;
						break;
					}
				}
				if (!hasPic)
					result.add(new GenericTriplet<String, Bitmap, Boolean>(s, null, false));
			}
		}		
		return result;
	}


	

	@Override
	public ArrayList<GenericPair<Bitmap, String>> getImagesAnrSoundsForObject(GenericPair<String, Boolean> object) {
		ArrayList<GenericPair<Bitmap, String>> result = new ArrayList<GenericPair<Bitmap, String>>();
		File objectsFolder;
		if (object.getSecond())		//object is single
		{
			objectsFolder = getObjectFile(object.getFirst(), true);
			if (!objectsFolder.exists())
				return null;
			if (!getObjectsPicturesAndSound(objectsFolder, result))
				return null;
		}
		else	// object is multi
		{						
			objectsFolder = getObjectFile(object.getFirst(), false);
			if (!objectsFolder.exists())
				return null;
			// get the single objects names file
			File singlesFile  = new File(objectsFolder, innerSinglesFileName);
			if (!singlesFile.exists())
				return null;		
			//read file line by line, each line is a name of an object
			String strLine;			
			try{				
				BufferedReader in = new BufferedReader(new FileReader(singlesFile));
				while ((strLine=in.readLine())!=null)
					if (strLine!="")
					{
						File singleFolder = getObjectFile(strLine, true);
						if (!singleFolder.exists())
							return null;
						if (!getObjectsPicturesAndSound(singleFolder, result))
							return null;
					}								
				in.close();
			} catch (Exception e) {
				return null;
			}		
		}
				
		return result;
	}
	
	
	/* the method inserts all the pictures in the objectsFolder into the list
	 *  return value is false if failed
	 */
	protected boolean getObjectsPicturesAndSound(File objectsFolder, ArrayList<GenericPair<Bitmap, String>> result)
	{	
		// get the images folder (it must exist by now)
		File imagesFolder = new File(objectsFolder, imageFolderName);
		if (!imagesFolder.exists())
			return false;
		File [] images = imagesFolder.listFiles();				
		File dataFile = new File(objectsFolder, objectDataFileName);
		if (!dataFile.exists())
			return false;
		String path = null;
		try{			
			String line=null;
			BufferedReader reader = new BufferedReader(new FileReader(dataFile));	
			// second line in data file is sound path, if such exists
			line = reader.readLine();	
			if (line.equals(notUsesDefaultSoundString))
			{
				line=reader.readLine();
				if (!line.equals(noSoundPathExists))
					path = line;
			}			
			reader.close();
		}catch(Exception e){			
			return false;
		}
		
		for (int i=0; i<images.length; ++i)
		{
			File image= images[i];
	    	result.add(new GenericPair<Bitmap, String>
	    					(BitmapManager.decodeBitmapFromFile(image.getAbsolutePath(), 256, 200), path));
		}
		return true;
	}
	
	
	/* the method creates all the main directories of the application, and returns false
	 * if it fails
	 */
	public boolean initializeDirectory()
	{
		File myDir, singleDir, multiDir;	
		//get the root directory for the application
		myDir = ctxt.getDir(dirName, 0);
		// get appropriate sub directory
		singleDir = new File(myDir, singlesDirName);
		if (!singleDir.exists())
			if (!singleDir.mkdirs())
				return false;
		multiDir = new File(myDir, multiDirName);
		if (!multiDir.exists())
			if (!multiDir.mkdirs())
				return false;

		return true;
	}
	
	
	
}
